[Previous] [Next]

ActiveX Controls for the Internet

Many programmers believe that the Internet is the natural habitat for ActiveX controls, so you might have been surprised that I haven't described Internet-specific features until the end of the chapter. The plain truth is that, Microsoft's plans notwithstanding, Microsoft Internet Explorer still is, as I write these pages, the only popular browser that natively supports ActiveX controls, at least without any plug-in modules. So if you heavily use ActiveX controls in HTML pages, you automatically reduce the number of potential users of your Web site. You see, ActiveX controls probably aren't very useful for the Internet, even though they might find their way into intranets, where administrators can be sure about which browser is installed on all client machines. As far as the Internet is concerned, however, Dynamic HTML and Active Server Pages seem to offer a better solution for building dynamic and "smart" pages, as I explain in the section devoted to Internet programming.

Programming Issues

In general, ActiveX controls in HTML pages can exploit the additional features provided by the browser in which they're running. In this section, I briefly describe the new methods and events that such controls can use. But first of all, you need to understand how an ActiveX control is actually placed in an HTML page.

ActiveX controls on HTML pages

You can place a control in a page using a number of HTML Page editors. For example, following is the code that Microsoft FrontPage produces for an HTML page that includes my ClockOCX.ocx control, whose source code is available on the companion CD. Notice that the control is referenced through its CLSID, not its more readable ProgID name. (The HTML code that refers to the ActiveX control is in boldface.)

<HTML>
<HEAD>
<TITLE>Home page</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H1>A web page with an ActiveX Control on it.</H1>
<OBJECT CLASSID="clsid:27E428E0-9145-11D2-BAC5-0080C8F21830"
    BORDER="0" WIDTH="344" HEIGHT="127">
    <PARAM NAME="FontName" VALUE="Arial">
    <PARAM NAME="FontSize" VALUE="24">
</OBJECT>
</BODY>
</HTML>

As you can see, all the information concerning the control is enclosed by the <OBJECT> and </OBJECT> tags, and all initial properties values are provided in <PARAM> tags. These values are made available to the control in its ReadProperties event procedure. (If there are no <PARAM> tags, the control could receive an InitProperties event instead, but the exact behavior depends on the browser.) ActiveX controls intended to be used on Web pages should always expose Fontxxxx properties instead of, or together with, the Font object property because assigning object properties in an HTML page isn't simple.

When you're using an ActiveX control on a Web site, many things can go wrong—for example, references to Extender properties that aren't available under the browser. Visual Basic 6 offers a couple of ways to reduce the guesswork when it's time to fix these errors. The first option is to start the component from within the IDE and wait until the browser creates an instance of the control. The second option is to have Visual Basic create an empty HTML page with just the ActiveX control on it and automatically load it into the browser. You can select these options in the Debugging tab of the Project Properties dialog box, as shown in Figure 17-18.

Figure 17-18. The Debugging tab of the Project Properties dialog box.

Hyperlinking

The UserControl object exposes the Hyperlink property, which returns a Hyperlink object that you can use to navigate to other HTML pages. The Hyperlink object exposes three methods, the most important of which is the NavigateTo method:

Hyperlink.NavigateTo Target, [Location], [FrameName]

Target is the URL to which you navigate, Location is an optional argument that points to a specific location in an HTML page, and FrameName is the optional name of a frame in a page. If the ActiveX control is running inside a browser, the new page is shown within the browser itself; if the control isn't running in a browser, the default browser is automatically launched.

The Hyperlink object exposes two more methods, GoBack and GoForward, which let you navigate the browser's history list. Unless you're absolutely sure that the history list isn't empty, you should always protect these methods with an On Error statement:

Private Sub cmdBack_Click()
    On Error Resume Next
    Hyperlink.GoBack
    If Err Then MsgBox "History is empty!"
End Sub

TIP
You can navigate many kinds of documents, not just HTML pages. For example, Internet Explorer can display Microsoft Word and Microsoft Excel files, so you can use it as a document browser, as the following code demonstrates:

Hyperlink.NavigateTo "C:\Documents\Notes.Doc"

Asynchronous download

ActiveX controls authored in Visual Basic support asynchronous downloading of properties. Let's say that you have a PictureBox-like ActiveX control that can read its contents from a GIF or BMP file. Instead of waiting for the image to be completely downloaded, you'll do better to start an asynchronous download operation and immediately return the control to the user. The key to asynchronous downloading is the AsyncRead method of the UserControl object, whose syntax is this:

AsyncRead Target, AsyncType, [PropertyName], [AsyncReadOptions]

Target is the URL of the property to be downloaded. AsyncType is the type of the property and can be one of the following values: 0-vbAsyncTypePicture (an image that can be assigned to a Picture property), 1-vbAsyncTypeFile (a file created by Visual Basic), or 2-vbAsyncTypeByteArray (a Byte array). PropertyName is the name of the property whose value is being downloaded and is useful when there are many properties that can be downloaded asynchronously. But keep in mind that there can be only one AsyncRead operation active at one time.

The AsyncRead method supports a new AsyncReadOptions argument, a bit-fielded integer that accepts the values listed in Table 17-1. Using this values you can fine-tune the performance of your asynchronous download operation and decide whether the control can use the data in the local cache.

Table 17-1. The available values for the AsyncReadOptions argument of the AsyncRead method.

Constant Value AsyncRead Behavior
vbAsyncReadSynchronousDownload 1 Returns only when the down load is complete (synchronous download).
vbAsyncReadOfflineOperation 8 Uses only the locally cached resource.
vbAsyncReadForceUpdate 16 Forces the download from the remote Web server, ignoring any copy in the local cache.
vbAsyncReadResynchronize 512 Updates the copy in the local cache only if the version on the remote Web server is more recent.
vbAsyncReadGetFromCacheIfNetFail &H80000 Uses the copy in the local cache if the connection to the remote Web server fails.

On the companion CD, you'll find the complete source code of a ScrollablePictureBox ActiveX control, which supports scrolling of large images as well as their asynchronous downloading from the Internet. (See Figure 17-19.) The asynchronous download feature is provided in the form of a PicturePath property that, when assigned, starts the downloading process:

Public Property Let PicturePath(ByVal New_PicturePath As String)
    m_PicturePath = New_PicturePath
    PropertyChanged "PicturePath"
    If Len(m_PicturePath) Then
        AsyncRead m_PicturePath, vbAsyncTypePicture, "Picture"
    End If
End Property

You can cancel an asynchronous download operation at any moment using the CancelAsyncRead method:

CancelAsyncRead "Picture"

Click to view at full size.

Figure 17-19. The ScrollablePictureBox control running inside Internet Explorer.

When the asynchronous download terminates, Visual Basic fires an AsyncReadComplete event in the UserControl module. This event receives an AsyncProperty object, whose most important properties are PropertyName and Value:

Private Sub UserControl_AsyncReadComplete(AsyncProp As AsyncProperty)
    If AsyncProp.PropertyName = "Picture" Then
        Set Image1.Picture = AsyncProp.Value
    End If
End Sub

The AsyncProperty object has been greatly enhanced in Visual Basic 6 and now includes properties such as BytesMax, ByteRead, Status, and StatusCode. For additional information, see the language documentation. Visual Basic 6 also exposes the AsyncReadProgress event, which fires when new data is available locally. You can use this event to display a progress bar that informs the user about the status of the operation:

Private Sub UserControl_AsyncReadProgress(AsyncProp As AsyncProperty)
    If AsyncProp.PropertyName = "Picture" Then
        Dim percent As Integer
        If AsyncProp.BytesMax > 0 Then
            percent = (AsyncProp.BytesRead * 100&) \ AsyncProp.BytesMax
        End If
    End If
End Sub

The AsyncReadProgress and AsyncReadComplete events fire immediately if the data is stored on a local disk (in this case, PicturePath is the path of a file) or if it is in the local cache. If you aren't downloading an image (therefore, AsyncProp.AsyncType is 1-vbAsyncTypeFile or 2-vbAsyncTypeByteArray), you can read and process the data while it's being downloaded. This arrangement slows the process slightly, but usually the overhead isn't noticeable. If you open a file, you must close it before exiting the event procedure, and you must avoid calling DoEvents to avoid reentrancy problems. The AsyncReadProgress and AsyncReadComplete events occur when the download is complete: You can learn when this happens in the AsyncReadProgress event by checking that the AsyncProp.StatusCode property returns the value 6vbAsyncStatusCodeEndDownloadData.

Accessing the browser

A control on an HTML page can do more than simply modify its appearance and behavior: It can manipulate the attributes of the page itself and of the other controls on it. You can access the container page using the Parent object, as this code does:

' Changing the HTML page's foreground and background colors
With Parent.Script.document
    .bgColor = "Blue"
    .fgColor = "White"
End With

You can also access and manipulate all the controls on the page using the ParentControls collection. But this method requires that you set the ParentControlsType property of ParentControls collection to the value vbNoExtender. This setting is necessary because Internet Explorer exposes an Extender object that can't be used from Visual Basic code.

I don't have enough room to describe all the things that you can do once you have a reference to the page that contains the ActiveX control. If you're interested, you should look for additional information on the Internet Explorer Scripting Object Model on the Microsoft Web site.

TIP
If you're writing a control that can be used on both regular forms and HTML pages, you need to know which container it's running in. You can do this by looking at the object returned by the Parent object:

' Test if the control runs in an HTML page.
If TypeName(Parent) = "HTMLDocument" Then ...

Show and Hide events

The Show event fires in the UserControl module when the page that contains it becomes visible, while the Hide event fires when the page becomes invisible but is still in the cache. Eventually, the page might become visible again, thus firing another Show event, or the browser might remove the page from the cache (for example, when the browser itself is closed), in which case the control receives a Terminate event.

Multithreaded ActiveX controls

If you're going to use the ActiveX control with Microsoft Explorer or a multithreaded Visual Basic application, you should make the control apartment-threaded by selecting the corresponding Threading Model option in the General tab of the Project Properties dialog box. Beware, however, of a documented bug: Multithreaded controls don't fire the Hide event when they run under Internet Explorer 4.0. For an ActiveX control to behave correctly, you must mark it as single-threaded and enable the Active Desktop option. For more information, see article Q175907 of the Microsoft Knowledge Base.

Component Download

When you're creating an HTML page that contains one or more ActiveX controls, you must provide a way for the browser to download and install the ActiveX control if it isn't already registered on the client machine.

Creating a package for deployment

The mechanism used for deploying the ActiveX controls on client machines is based on Cabinet (CAB) files. CAB files are compressed files that can include multiple ActiveX controls (as well as other types of files, such as EXEs and DLLs) and that can be digitally signed if necessary. You create CAB files by running the Package and Deployment Wizard and selecting Internet Package in its second step. The wizard also creates a sample HTM file that you can use as a model for the page that will host the control. This file contains the correct value for the CODEBASE attribute, which informs the browser of the name of the CAB file and the version of the ActiveX control. The browser then downloads the CAB file if the control with that CLSID isn't registered on the client machine or if its version is older than the one specified in the HTML page. This is a portion of the sample HTML file created for the ClockOCX control:

<OBJECT ID="Clock"
CLASSID="CLSID:27E428E0-9145-11D2-BAC5-0080C8F21830"
CODEBASE="ClockOCX.CAB#version=1,0,0,0">
</OBJECT>

CAB files can embed all the ancillary files that the ActiveX control needs to work properly, including data files and satellite DLLs. The list of dependencies of an ActiveX control is described in an INF file, which is produced by the Package and Deployment Wizard and also included in the CAB file itself.

ActiveX controls authored in Visual Basic also require the Visual Basic runtime files. The default option in the Package and Deployment Wizard instructs the installing procedure to download the runtime files from the Microsoft Web site. This setting ensures that the user always receives the most recent version of those files and also reduces the burden on your Web site.

Safety

When an ActiveX control is running in the browser, it could do all sort of evil things to the user's system, such as deleting system files, trashing the Registry, or stealing confidential data. You must, therefore, assure users that not only are your controls not so rude, but also that no other developer can use your controls to damage the machines they're running on.

To broadcast the promise that your control doesn't (and can't) misbehave, you can mark it as "Safe for initialization" or "Safe for scripting." If you declare that your control is safe for initialization, you're telling the browser that there's no way for an HTML page author to accidentally or intentionally do any harm by assigning values to the control's properties through the <PARAM> tags in the <OBJECT> section of the page. If you mark your control as safe for scripting, you're going a bit further because you're declaring that there's no way for a script on the page to set a property or call a method that can damage the system. By default, Microsoft Internet Explorer refuses to download components that aren't marked as safe for initialization and safe for scripting.

Marking your control as safe for initialization or safe for scripting isn't a decision that you should take lightly. The fact that your control doesn't purposely do any damage isn't enough in most cases. Just to give you an idea of the subtleties that you must account for, imagine these scenarios:

You mark your component as safe for initialization or safe for scripting in the Package and Deployment Wizard, as shown in Figure 17-20.

TIP
You can quickly learn which ActiveX controls on your machine are safe for initialization or for scripting by using the OleView utility that comes with Visual Studio. This is the portion of the Registry that marks a control as safe:

HKEY_CLASSES_ROOT
  \CLS
    \<your control's CLSID>
      \Implemented Categories
        \{7DD95802-9882-11CF-9FA9-00AA006C42C4}
        \{7DD95801-9882-11CF-9FA9-00AA006C42C4}

The last two lines of the listing indicate safe for initialization and safe for scripting, respectively. Once you know how this information is recorded in the Registry, you can use the Regedit utility to modify these setting by adding or removing these keys.

Click to view at full size.

Figure 17-20. The Package and Deployment Wizard lets you mark your controls as Safe For Initialization and Safe For Scripting.

A more sophisticated way to address the safety problem is through the IObjectSafety ActiveX interface, which allows your component to programmatically specify which methods and properties are safe. This approach offers greater flexibility than just marking the component as safe. This is an advanced topic, however, and I won't cover it in this book.

Digital signatures

It's obvious that marking a control to be safe isn't enough for most users. After all, anyone can mark a control as safe. Even if they trust your good intentions and your ability as a programmer, they can't be absolutely sure that the control is actually coming from you or that it hasn't been tampered with after you compiled it.

Microsoft has solved this problem by making it possible for you to add a digital signature to ActiveX controls by using a public key encryption algorithm. To digitally sign a control, you need a private encoding key, which you obtain from a company that issues digital certificates—for example, VeriSign Inc. You must pay a fee to obtain such certificates, but they are quite affordable even for individual developers. For more information, pay a visit to http://www.verisign.com. Once you have obtained a certificate, you can sign your control—or, most likely, its CAB file—using the SignCode utility which is included in the ActiveX SDK. You can add a digital signature to EXE, DLL, and OCX files, but you need to do so only if you plan to distribute them without packaging them in a CAB file.

Licensing

ActiveX controls can be sold as part of a business application to users or as stand-alone components to other developers. In the latter case, your customers should be able to use the control at design time and also redistribute it with their own applications. If you don't want their customers to be able to redistribute your control, you need to add a license key to your control.

The Require License Key option

If you tick the Require License Key option on the General tab of the Project Properties dialog box and then compile the ActiveX control, Visual Basic generates a VBL (Visual Basic License) file that contains the license for the control. For example, this is the VBL file generated for the ClockOCX control:

REGEDIT
HKEY_CLASSES_ROOT\Licenses = Licensing: Copying the keys may be a violation
of established copyrights.
HKEY_CLASSES_ROOT\Licenses\27E428DE-9145-11D2-BAC5-0080C8F21830 = 
geierljeeeslqlkerffefeiemfmfglelketf

As you see, a VBL file is nothing but a script for the Registry. When you create a standard installation procedure, the Wizard includes this file in the package. When other developers buy your control and install it on their machines, the installation routine uses this file to patch their Registries but won't copy the file on their hard disks. For this reason, when they redistribute your control as part of their applications, the VBL isn't included in the deployment package and their customers won't be able to use the control at design time (unless, of course, they buy a license from you).

A control that requires a license key always looks for this key when it's instantiated. If the control is used in a compiled program, the license key is included in the executable EXE file. But if the control is used in an interpreted environment, no executable file can provide the key and the control has to look for it in the Registry. This means that to use the control on a Visual Basic form or in a Microsoft Office application (or another VBA-powered environment), you need the license to be installed in the Registry.

If your control includes other ActiveX controls as constituent controls, you should license them for distribution as well; otherwise, your control won't correctly work at design time. Of all the controls included in the Visual Basic package, the only one that you can't redistribute is the DBGrid control. Note, however, that the Microsoft License Agreement specifies that you can use Microsoft controls in your ActiveX control only if you significantly expand their functionality. I never found anywhere how that "significantly" can be measured, though.

License keys for controls on Web pages

The mechanism that I've just described doesn't address the particular nature of ActiveX controls on a Web page. In fact, it doesn't make sense to require that the user machine have the control's license key installed in the Registry. Nor do you want to send the license key with the control in a readable form in the HTML page. The solution to this difficulty comes in the form of a License Package File (or LPK file for short). You create this file by using the Lpk_Tool.Exe utility that you can find in the \Common\Tools\Vb\Lpk_Tool subdirectory. (See Figure 17-21.) Once you have created an LPK file, you reference it with a parameter to the <PARAM> tag, as follows:

<PARAM NAME="LPKPath" VALUE="ClockOCX.lpk">

This parameter tells the browser where it can download the license key of the ActiveX control; the license key is transferred each time the page is downloaded because the license keys of ActiveX controls found on HTML pages are never added to the client machine's Registry. The value of the LPKPath parameter can be a relative or an absolute URL, but in the latter case you might have problems when moving the HTM file to another location of your site. The owner of the Web site must have purchased a license for your ActiveX control to be able to send it in HTML pages. In other words, as far as the license mechanism is concerned, Web site owners are regarded as developers.

Click to view at full size.

Figure 17-21. The Lpt_Tool utility can create an LPK file containing the license keys of one or more ActiveX controls.

NOTE
It should be made clear that the license mechanism provided by Visual Basic isn't bulletproof. After all, a malicious developer has only to copy the VBL from the installation diskette or—if that file is no longer available—retrieve the relevant information from the Registry and re-create the VBL file. Actually, the only thing you can be sure of is that the license key won't be accidentally included in an installation procedure. If you need a more secure system, you should devise an alternative method based on alternate locations of the Registry or custom license files loaded in system directories.

If you have carefully read Chapter 16 and this chapter, you might be surprised to see how few features Visual Basic 6 has added to those already available in Visual Basic 5. But you see the real potential of components and controls when you add ADO to the equation and begin to build data-aware classes and components. These new capabilities are described in the next chapter.